package com.network;

import com.drawquest.test.ReflectionUtils;
import com.drawquest.test.ShouldBeMocked;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Random;
import org.apache.commons.io.IOUtils;
import org.mockito.Mockito;

/**
 * @author sad
 */
public abstract class AbstractTst {

    public String getTestFileString(String file) {
        InputStream str = this.getClass().getResourceAsStream(file);
        try {
            if (str == null) {
                throw new RuntimeException("Resource test file " + file + " is not found");
            }
            return IOUtils.toString(str);
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        } finally {
            try {
                str.close();
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }
        }
    }

    protected void setField(Object obj, String field, Object value) {
        if (obj == null) {
            throw new RuntimeException("Object cannot be null");
        }

        Field f = getField(obj.getClass(), field);
        try {
            f.set(obj, value);
        } catch (IllegalArgumentException | IllegalAccessException ex) {
            throw new RuntimeException(ex);
        }
    }

    private Field getField(Class clazz, String field) {
        Class tClazz = clazz;
        while (tClazz != null) {
            try {
                Field f = tClazz.getDeclaredField(field);
                f.setAccessible(true);
                return f;
            } catch (NoSuchFieldException ex) {
            } catch (SecurityException ex) {
                throw new RuntimeException(ex);
            }

            tClazz = tClazz.getSuperclass();
        }
        throw new RuntimeException("Field " + field + " is not found in class " + clazz.getName());
    }

    public static void setPredefinedRandom(int seed) {
        try {
            Field field = Math.class.getDeclaredField("randomNumberGenerator");
            field.setAccessible(true);
            field.set(null, new Random() {
                @Override
                public double nextDouble() {
                    return 1;
                }
            });
        } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException ex) {
            throw new RuntimeException(ex);
        }
    }

    /**
     * Inject all fields that annotated as @ShouldBeMocked to the testObject and also instantiate it in test class
     */
    protected void initMocks(Object testObject) {
        for (Field f : ReflectionUtils.getAllFields(this.getClass())) {
            if (f.getAnnotation(ShouldBeMocked.class) != null) {
                if (Modifier.isFinal(f.getModifiers())) {
                    throw new RuntimeException(String.format("Field %s that should be mocked in class %s cannot be final", f.getName(), this.getClass().getSimpleName()));
                }
                Object mock = Mockito.mock(f.getType());
                f.setAccessible(true);
                try {
                    f.set(this, mock);
                    setField(testObject, f.getName(), mock);
                } catch (IllegalArgumentException | IllegalAccessException ex) {
                    throw new RuntimeException(ex);
                }
            }
        }
    }
}
